﻿using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.Maui.ApplicationModel;
using OpenCvSharp;
using System.Diagnostics;
using System.Reactive;
using System.Threading;
using Windows.Media.Playback;
using ymk_camera_surveillance.Models;
using static ymk_camera_surveillance.Models.ObsCamera;

namespace ymk_camera_surveillance.CaptureCamera
{
    public class MatReadyMessage : CommunityToolkit.Mvvm.Messaging.Messages.ValueChangedMessage<int>
    {
        public MatReadyMessage(int readyIndex) : base(readyIndex)
        {
        }
    }

    public class CapRtsp
    {
        enum AVHWDeviceType
        {
            AV_HWDEVICE_TYPE_NONE,
            AV_HWDEVICE_TYPE_VDPAU,
            AV_HWDEVICE_TYPE_CUDA,
            AV_HWDEVICE_TYPE_VAAPI,
            AV_HWDEVICE_TYPE_DXVA2,
            AV_HWDEVICE_TYPE_QSV,
            AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
            AV_HWDEVICE_TYPE_D3D11VA,
            AV_HWDEVICE_TYPE_DRM,
            AV_HWDEVICE_TYPE_OPENCL,
            AV_HWDEVICE_TYPE_MEDIACODEC,
            AV_HWDEVICE_TYPE_VULKAN,
        };

        private string rtspUrl = "";
        public CameraStatus status = CameraStatus.DISCONNECT;
        private VideoCapture capture = null;

        private readonly Mat TmpMat = new Mat();
        private readonly List<Mat> ReadyMats = new List<Mat>();
        private volatile int readyIndex = 0;
        private readonly int readySize = 10;

        private readonly Object lockStartCapture = new Object();
        private Thread readCapThread = null;


        public CapRtsp()
        {
            for (int i = 0; i < readySize; i++)
            {
                ReadyMats.Add(new Mat());
            }
        }

        public bool StartCapture(string rtspUrl)
        {
            var res = false;
            lock (lockStartCapture)
            {
                try
                {
                    if (capture != null)
                    {
                        if (capture.IsOpened() && !capture.IsDisposed)
                        {
                            this.status = CameraStatus.DISCONNECT;
                            capture.Dispose();
                            capture = null;
                        }
                    }
                    //string rtspStr = "rtsp://admin:lmk3312796@192.168.1.64:554/live0";
                    var prms = new VideoCapturePara(videoAcceleration: VideoAccelerationType.Any, deviceIndex: -1);
                    capture = new VideoCapture(rtspUrl, apiPreference: VideoCaptureAPIs.FFMPEG, prms: prms);

                    // CAP_PROP_BUFFERSIZE
                    capture.Set(38, 0);
                    if (!capture.IsOpened())
                    {
                        this.status = CameraStatus.OPEN_FAIL;
                        res = false;
                        return res;
                    }
                    //FFMPEG
                    var backend = capture.GetBackendName();
                    // none 0 未启用加速
                    // D3D11 2 window 硬件加速
                    var hwAccele = (VideoAccelerationType)capture.Get(VideoCaptureProperties.HwAcceleration);
                    var hwDevice = capture.Get(VideoCaptureProperties.HwDevice);
                    var fps = capture.Get(VideoCaptureProperties.Fps);

                    this.status = CameraStatus.OPEN_SUCCESS;
                    this.rtspUrl = rtspUrl;
                    StartReadCapture();
                    res = true;
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    res = false;
                }
            }
            return res;
        }

        public void StartReadCapture()
        {
            lock (lockStartCapture)
            {
                if (readCapThread == null)
                {
                    ThreadStart threadStart = new ThreadStart(ReadCapture);
                    Thread thread = new Thread(threadStart);
                    thread.Start();
                    readCapThread = thread;
                }
                Debug.WriteLine("pass StartReadCapture for same capture");
            }
        }

        private void ReadCapture()
        {
            // 进入读取视频每镇的循环
            while (true)
            {
                try
                {
                    bool res;
                    try
                    {
                        res = capture.Read(TmpMat);
                        var matTime = capture.Get(VideoCaptureProperties.PosMsec);
                        var fps = capture.Get(VideoCaptureProperties.Fps);
                        //buffsize
                        var buffsize = capture.Get(38);

                        var matTimeDT = DateTimeOffset.FromUnixTimeMilliseconds((int)matTime);
                        Debug.WriteLine($"图片时间 {matTimeDT.LocalDateTime.ToString("yyyy/MM/dd HH:mm:ss:ffff")}  帧率: {fps} buff {buffsize}");

                        long timeStamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); // 相差毫秒数
                        DateTimeOffset dto = DateTimeOffset.FromUnixTimeMilliseconds(timeStamp);
                        Debug.WriteLine($"read mat 时间 {dto.LocalDateTime.ToString("yyyy/MM/dd HH:mm:ss:ffff")}  帧率: {fps} buff {buffsize}");

                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.ToString());
                        goto Failed;
                    }
                    //判断是否还有没有视频图像
                    if (res == false || TmpMat.Empty())
                    {
                        goto Failed;
                    }
                    this.status = CameraStatus.CONNECTED;
                    var mutReadyIndex = readyIndex;
                    TmpMat.CopyTo(ReadyMats[readyIndex]);
                    // send msg
                    //Debug.WriteLine($"发送消息， token: {rtspUrl}, readyIndex: {readyIndex}");
                    var reayMat = ReadyMats[readyIndex];
                    if (reayMat != null && reayMat.Data != 0)
                    {
                        WeakReferenceMessenger.Default.Send(new MatReadyMessage(mutReadyIndex), rtspUrl);
                        long timeStamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); // 相差毫秒数
                        Debug.WriteLine($"发送消息时间 {timeStamp}");

                    }
                    else
                    {
                        Debug.WriteLine($"跳过 无效 数据帧， reayMat.Data: {reayMat.Data}, readyIndex: {readyIndex}");
                    }
                    readyIndex += 1;
                    readyIndex %= readySize;
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    goto Failed;
                }

                continue;
            Failed:
                lock (lockStartCapture)
                {
                    this.status = CameraStatus.DISCONNECT;
                    ReConnect();
                }
            }
        }

        public CameraStatus GetState()
        {
            return this.status;
        }

        public Mat GetReadyMat(int readyIndex)
        {
            return ReadyMats[readyIndex];
        }

        public void ReConnect()
        {
            lock (lockStartCapture)
            {

                if (this.status != ObsCamera.CameraStatus.CONNECTED && this.status != ObsCamera.CameraStatus.OPEN_SUCCESS)
                {
                    StartCapture(this.rtspUrl);
                }
            }

        }
    }
}
